﻿using Furion.DynamicApiController;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FurStudy.Application
{
    /// <summary>
    /// 内存缓存
    /// </summary>
    public class CacheServices : IDynamicApiController
    {
        private const string _timeCacheKey = "cache_time";

        private readonly IMemoryCache _memoryCache;

        public CacheServices(IMemoryCache memoryCache)
        {
            _memoryCache = memoryCache;
        }

        /// <summary>
        /// 设置缓存选项
        /// 缓存当前时间
        /// </summary>
        /// <returns></returns>
        [ApiDescriptionSettings(KeepName = true)] // 接口描述设置：保留原有名称
        public DateTimeOffset GetOrCreateAsync()
        {
            // 保证数据的缓存时间不超过绝对时间
            return _memoryCache.GetOrCreate(_timeCacheKey, entry => 
            {
                // 关于缓存时间：
                // 只有具有可调过期的缓存项集存在过时的风险。 如果访问的时间比滑动过期时间间隔更频繁，则该项将永不过期。
                // 将弹性过期与绝对过期组合在一起，以保证项目在其绝对过期时间通过后过期。 绝对过期会将项的上限设置为可缓存项的时间，同时仍允许项在可调整过期时间间隔内未请求时提前过期。
                // 如果同时指定了绝对过期和可调过期时间，则过期时间以逻辑方式运算。 如果滑动过期时间间隔 或 绝对过期时间通过，则从缓存中逐出该项。

                entry.SlidingExpiration = TimeSpan.FromSeconds(3); // 滑动缓存时间
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(20); // 获取或插入相对于现在的时间绝对过期时间

                // 手动设置缓存选项
                var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(3));
                _memoryCache.Set(_timeCacheKey, DateTimeOffset.UtcNow, cacheEntryOptions);

                return DateTimeOffset.UtcNow;
            });

            // 内存缓存支持设置缓存时间、缓存大小、及绝对缓存过期时间等

            //return await _memoryCache.GetOrCreateAsync(_timeCacheKey, async entry =>
            //{
            //    // 这里可以使用异步
            //});
        }

        #region 缓存依赖关系

        // 下面的示例演示如何在依赖条目过期时使缓存条目过期。 CancellationChangeToken添加到缓存的项。 当Cancel在上调用时CancellationTokenSource，将逐出两个缓存项。
        // 使用CancellationTokenSource 允许将多个缓存条目作为一个组逐出。 using 在下面的代码中，在块中创建的缓存条目using将继承触发器和过期设置。

        //public IActionResult CreateDependentEntries()
        //{
        //    var cts = new CancellationTokenSource();
        //    _memoryCache.Set(CacheKeys.DependentCTS, cts);

        //    using (var entry = _memoryCache.CreateEntry(CacheKeys.Parent))
        //    {
        //        // expire this entry if the dependant entry expires.
        //        entry.Value = DateTime.Now;
        //        entry.RegisterPostEvictionCallback(DependentEvictionCallback, this);

        //        _memoryCache.Set(CacheKeys.Child,
        //            DateTime.Now,
        //            new CancellationChangeToken(cts.Token));
        //    }

        //    return RedirectToAction("GetDependentEntries");
        //}

        //public IActionResult GetDependentEntries()
        //{
        //    return View("Dependent", new DependentViewModel
        //    {
        //        ParentCachedTime = _cache.Get<DateTime?>(CacheKeys.Parent),
        //        ChildCachedTime = _cache.Get<DateTime?>(CacheKeys.Child),
        //        Message = _cache.Get<string>(CacheKeys.DependentMessage)
        //    });
        //}

        //public IActionResult RemoveChildEntry()
        //{
        //    _memoryCache.Get<CancellationTokenSource>(CacheKeys.DependentCTS).Cancel();
        //    return RedirectToAction("GetDependentEntries");
        //}

        //private static void DependentEvictionCallback(object key, object value,
        //    EvictionReason reason, object state)
        //{
        //    var message = $"Parent entry was evicted. Reason: {reason}.";
        //    ((HomeController)state)._cache.Set(CacheKeys.DependentMessage, message);
        //}

        #endregion
    }
}
